﻿using System;
using System.Collections.Generic;
using System.Text;

namespace BCGPSCommon
{
    /// <summary>
    /// GPS坐标与高德坐标相互转换（GCJ02到WGS84标准互转）
    /// </summary>
    /// WGS-84：是国际标准，GPS坐标（Google Earth使用、或者GPS模块）
    ///GCJ-02：中国坐标偏移标准，Google Map、高德、腾讯使用
    ///BD-09：百度坐标偏移标准，Baidu Map使用

   ///这些坐标系是对真实坐标系统进行人为的加偏处理，按照特殊的算法，将真实的坐标加密成虚假的坐标，而这个加偏并不是线性的加偏，所以各地的偏移情况都会有所不同，具体的内部实现是没有对外开放的，但是坐标之间的转换算法是对外开放，在网上可以查到的，此算法的误差在0.1-0.4之间。
    public class GCJ02_WGS84_TransTools
    {
        public static double pi = 3.1415926535897932384626;//圆周率
        public static double a = 6378245.0;//克拉索夫斯基椭球参数长半轴a
        public static double ee = 0.00669342162296594323;//克拉索夫斯基椭球参数第一偏心率平方

        /// <summary>
        /// 从GPS转高德
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        /// <returns></returns> 
        public LocateInfo wgs84_To_Gcj02(double lat, double lon)
        {
            LocateInfo info = new LocateInfo();
            if (isOutOfChina(lat, lon))
            {
                info.setChina(false);
                info.setLatitude(lat);
                info.setLongitude(lon);
            }
            else
            {
                double dLat = transformLat(lon - 105.0, lat - 35.0);
                double dLon = transformLon(lon - 105.0, lat - 35.0);
                double radLat = lat / 180.0 * pi;
                double magic = Math.Sin(radLat);
                magic = 1 - ee * magic * magic;
                double sqrtMagic = Math.Sqrt(magic);
                dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
                dLon = (dLon * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * pi);
                double mgLat = lat + dLat;
                double mgLon = lon + dLon;
                info.setChina(true);
                info.setLatitude(mgLat);
                info.setLongitude(mgLon);
            }
            return info;
        }
        /// <summary>
        /// GPS转换百度
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        /// <returns></returns>
        public LocateInfo wgs84_To_Bd09(double lat, double lon)
        {
            LocateInfo info = new LocateInfo();
            if (isOutOfChina(lat, lon))
            {
                info.setChina(false);
                info.setLatitude(lat);
                info.setLongitude(lon);
            }
            else
            {
                double x = lon, y = lat;
                double z = Math.Sqrt(x * x + y * y) + 0.00002 * Math.Sin(y * pi);
                double theta = Math.Atan2(y, x) + 0.000003 * Math.Cos(x * pi);
                double tempLon = z * Math.Cos(theta) + 0.0065;
                double tempLat = z * Math.Sin(theta) + 0.006;

                info.setChina(true);
                info.setLatitude(tempLat);
                info.setLongitude(tempLon);
            }
            return info;
        }

        /// <summary>
        /// 百度转GPS
        /// </summary>
        /// <param name="bd_lat"></param>
        /// <param name="bd_lon"></param>
        /// <returns></returns>
        public LocateInfo bd09_To_Gps84(double bd_lat, double bd_lon)
        {

            LocateInfo gcj02 = bd09_To_Gcj02(bd_lat, bd_lon);
            LocateInfo map84 = gcj02_To_Wgs84(gcj02.Latitude,gcj02.longitude);
            return map84;

        }

        /// <summary>
        /// 高德转百度
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        /// <returns></returns>
        public LocateInfo Gcj02_To_bd09(double lat, double lon)
        {
            LocateInfo loc = gcj02_To_Wgs84(lat, lon);
            return wgs84_To_Bd09(loc.Latitude, loc.longitude);
        }


        private LocateInfo bd09_To_Gcj02(double bd_lat, double bd_lon)
        {
            double x = bd_lon - 0.0065, y = bd_lat - 0.006;
            double z = Math.Sqrt(x * x + y * y) - 0.00002 * Math.Sin(y * pi);
            double theta = Math.Atan2(y, x) - 0.000003 * Math.Cos(x * pi);
            double gg_lon = z * Math.Cos(theta);
            double gg_lat = z * Math.Sin(theta);
            return new LocateInfo()
            {
                Latitude = gg_lat,
                longitude = gg_lon,
            };
        } 
        
        /// <summary>
        /// 高德转到GPS
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        /// <returns></returns>
        public LocateInfo gcj02_To_Wgs84(double lat, double lon)
        {
            LocateInfo info = new LocateInfo();
            LocateInfo gps = transform(lat, lon);
            double lontitude = lon * 2 - gps.getLongitude();
            double latitude = lat * 2 - gps.getLatitude();
            info.setChina(gps.isChina());
            info.setLatitude(latitude);
            info.setLongitude(lontitude);
            return info;
        }
        // 判断坐标是否在国外
        private bool isOutOfChina(double lat, double lon)
        {
            if (lon < 72.004 || lon > 137.8347)
                return true;
            if (lat < 0.8293 || lat > 55.8271)
                return true;
            return false;
        }
        //转换
        private LocateInfo transform(double lat, double lon)
        {
            LocateInfo info = new LocateInfo();
            if (isOutOfChina(lat, lon))
            {
                info.setChina(false);
                info.setLatitude(lat);
                info.setLongitude(lon);
                return info;
            }
            double dLat = transformLat(lon - 105.0, lat - 35.0);
            double dLon = transformLon(lon - 105.0, lat - 35.0);
            double radLat = lat / 180.0 * pi;
            double magic = Math.Sin(radLat);
            magic = 1 - ee * magic * magic;
            double sqrtMagic = Math.Sqrt(magic);
            dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
            dLon = (dLon * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * pi);
            double mgLat = lat + dLat;
            double mgLon = lon + dLon;
            info.setChina(true);
            info.setLatitude(mgLat);
            info.setLongitude(mgLon);

            return info;
        }

        //转换纬度所需
        private double transformLat(double x, double y)
        {
            double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
                    + 0.2 * Math.Sqrt(Math.Abs(x));
            ret += (20.0 * Math.Sin(6.0 * x * pi) + 20.0 * Math.Sin(2.0 * x * pi)) * 2.0 / 3.0;
            ret += (20.0 * Math.Sin(y * pi) + 40.0 * Math.Sin(y / 3.0 * pi)) * 2.0 / 3.0;
            ret += (160.0 * Math.Sin(y / 12.0 * pi) + 320 * Math.Sin(y * pi / 30.0)) * 2.0 / 3.0;
            return ret;
        }
        //转换经度所需
        private double transformLon(double x, double y)
        {
            double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
                    * Math.Sqrt(Math.Abs(x));
            ret += (20.0 * Math.Sin(6.0 * x * pi) + 20.0 * Math.Sin(2.0 * x * pi)) * 2.0 / 3.0;
            ret += (20.0 * Math.Sin(x * pi) + 40.0 * Math.Sin(x / 3.0 * pi)) * 2.0 / 3.0;
            ret += (150.0 * Math.Sin(x / 12.0 * pi) + 300.0 * Math.Sin(x / 30.0 * pi)) * 2.0 / 3.0;
            return ret;
        }
    }

    public class LocateInfo
    {
        /**
         * 经度
         */
        public double longitude;
        /**
         * 维度
         */
        public double Latitude;
        /**
         * 是否在中国
         */
        public bool isInChina;

        public void setChina(bool value)
        {
            isInChina = value;
        }

        public void setLatitude(double value)
        {
            Latitude = value;
        }

        public void setLongitude(double value)
        {
            longitude = value;
        }

        public double getLongitude()
        {
            return longitude;
        }

        public double getLatitude()
        {
            return Latitude;
        }

        public bool isChina()
        {
            return isInChina;
        }
         
    }
}
